/*
 * Created by Jaren at 2022/1/27 19:16
 */
import 'dart:io';
import 'package:inmotion_protocol/base/protocol.dart';
import 'package:inmotion_protocol/common/exceptions.dart';

abstract class IFileManager {
  /// 打开文件方式 - 只读
  static const int openFileTypeReadOnly = 0;

  /// 打开文件方式 - 读写
  static const int openFileTypeRadWrite = 1;

  /// 打开文件方式 - 读写, 不存在则创建
  static const int openFileTypeReadWrite2 = 2;

  static const int stepSuccess = 0;
  static const int stepCancel = -1;
  static const int stepOpenFile = 1;
  static const int stepTransferFile = 2;
  static const int stepCheckFile = 3;
  static const int stepCloseFile = 4;
  static const int stepSetFileOffset = 5;

  /// 超时错误码
  static const int errorTimeout = 0xFF;

  /// 发送文件过程中出现本地IO错误
  static const int errorSendFileIOError = 0xFE;

  /// 未知错误
  static const int errorUnknown = 0xF0;

  /// 最大重试次数
  static const int defaultMaxRetryTimes = 5;

  /// 重试延时
  static const int defaultRetryDelayMills = 100;

  /// 被动中断发送标识位 see{@link IFileManager#interrupt}
  bool interrupted = false;

  /// 协议接口
  Protocol protocol;

  /// 开始传输时间
  int startedTimestamp = 0;

  /// 上次计算实时速度时间
  int lastRtSpeedTimestamp = 0;

  /// 上次计算实时速度时的传输进度
  int lastRtSpeedProgress = 0;

  /// 计算实时速度的时间段
  int calRtSpeedTimeInterval = 1000;

  int rtSpeed = -1;
  int aveSpeed = -1;
  int remainSeconds = -1;
  int costSeconds = 0;

  int transferredLength = 0;

  void init(FileConfig config);

  Stream<FileEvent> start();

  void destroy() {}

  int now() {
    return DateTime.now().millisecondsSinceEpoch;
  }

  IFileManager(this.protocol);

  bool checkInterrupted() {
    return interrupted;
  }

  /// 中断当前操作
  void interrupt() {
    interrupted = true;
  }

  int maxRetryTimes() {
    return defaultMaxRetryTimes;
  }

  int retryDelayMills() {
    return defaultRetryDelayMills;
  }

  int sizePeerPkg();

  int calRemainSeconds() {
    return 0;
  }

  int calAveSpeed() {
    return 0;
  }

  int calRtSpeed() {
    return -1;
  }

  void onStarted() {
    startedTimestamp = now();
  }

  void onTransferring(int progress, int lastTransferredLength, int total) {
    transferredLength += lastTransferredLength;
    if (lastRtSpeedTimestamp == 0) {
      lastRtSpeedTimestamp = now();
      return;
    }
    int nowMs = now();

    int pastTime = nowMs - lastRtSpeedTimestamp;
    if (pastTime >= calRtSpeedTimeInterval) {
      /// 超过计算实时速度的时间间隔，重新计算
      int progress2Cal = progress - lastRtSpeedProgress;
      int pastUntilLastRtSpeedCalSeconds = (pastTime ~/ 1000);
      if (pastUntilLastRtSpeedCalSeconds > 0) rtSpeed = (progress2Cal - lastRtSpeedProgress) ~/ pastUntilLastRtSpeedCalSeconds;
      costSeconds = ((nowMs - startedTimestamp) ~/ 1000);
      if (costSeconds > 0) aveSpeed = transferredLength ~/ costSeconds;
      if (aveSpeed > 0) remainSeconds = (total - transferredLength) ~/ aveSpeed;
    }
  }

  void reset() {
    interrupted = false;
    startedTimestamp = 0;
    lastRtSpeedTimestamp = 0;
    lastRtSpeedProgress = 0;
    calRtSpeedTimeInterval = 1000;
    rtSpeed = -1;
    aveSpeed = -1;
    remainSeconds = -1;
    costSeconds = 0;
    transferredLength = 0;
  }
}

abstract class FileConfig {
  static final File invalidFile = File('');
  File localFile = invalidFile;
}

class CanceledError implements Exception {
  @override
  String toString() {
    return "CanceledError";
  }
}

class FileEvent {
  static const fileOpened = 1;
  static const fileProgress = 2;
  static const prepareFileChecking = 3;
  static const fileChecked = 4;
  static const fileClosed = 5;

  int type;

  FileEvent(this.type);

  FileEvent.byFileOpened() : this(fileOpened);

  FileEvent.byFileChecked() : this(fileChecked);

  FileEvent.byFileClosed() : this(fileClosed);

  @override
  String toString() {
    return 'FileEvent{type: $type}';
  }
}

class FileOpenEvent extends FileEvent {
  FileOpenEvent(int type) : super(type);
}

/// 文件传输进度事件
class FileProgressEvent extends FileEvent {
  /// 当前进度
  int progress;

  /// 总大小
  int total;

  /// 平均速度
  int aveSpeed;

  /// 实时速度
  int rtSpeed;

  /// 已用时间
  int costSeconds;

  /// 预计剩余时间
  int remainSeconds;

  FileProgressEvent(this.progress, this.total, this.aveSpeed, this.rtSpeed, this.costSeconds, this.remainSeconds) : super(FileEvent.fileProgress);

  @override
  String toString() {
    return 'FileProgressEvent{progress: $progress, total: $total, aveSpeed: $aveSpeed, rtSpeed: $rtSpeed, costSeconds: $costSeconds, remainSeconds: $remainSeconds}';
  }
}

class FileOperationStepError extends InMotionProtocolException {
  static const noExtra = 0xFFFFFFFF;

  int step;
  int error;
  int extra = noExtra;

  FileOperationStepError(this.step, this.error);

  FileOperationStepError.withExtra(this.step, this.error, this.extra);

  @override
  String toString() {
    return "FileOperationStepError{step: $step, error: $error, extra: $extra}";
  }
}

/// 文件操作拦截器
abstract class ActionHook {
  Future run(FileEvent event, Object? data);
}
